/*
 * Equipo creador por Hector Adrian Valdecantos
 * emai: hvaldecantos@gmail.com
 * 30 de Mayo del 2009
 */

package org.javahispano.javacup.tacticas_aceptadas.hector.equipohectorvaldecantos;

import org.javahispano.javacup.modelo.*;

public class Pelota {
	private SituacionPartido sp;
	private Historico<Posicion> posiciones;
	private Historico<Double> alturas;
	private int indiceArqueroRival = 0;

	public Pelota(SituacionPartido sp) {
		this.sp = sp;
		posiciones = new Historico<Posicion>();
		alturas = new Historico<Double>();
		setIndiceArqueroRival();
	}

	public Posicion getPosicion() {
		return sp.balon();
	}

	public Posicion getPosicionAnterior() {
		try {
			return posiciones.getDelMedio();
		} catch (IndexOutOfBoundsException e) {
			return new Posicion();
		}
	}

	private void setIndiceArqueroRival() {
		indiceArqueroRival = Constantes.centroArcoSup.indiceMasCercano(sp.rivales());
	}

	public int getIndiceArqueroRival() {
		return indiceArqueroRival;
	}

	public void actualizarDatos() {
		posiciones.addElemento(sp.balon());
		alturas.addElemento(sp.alturaBalon());
	}

	public double getVelocidad() {
		double velocidad;
		try {
			velocidad = posiciones.getDelMedio().norma(posiciones.getMasNuevo()) / 1;
		} catch (IndexOutOfBoundsException e) {
			velocidad = 0.0;
		}
		return velocidad;
	}

	public double getVelocidadAnterior() {
		double velocidad;
		try {
			velocidad = posiciones.getMasAntiguo().norma(posiciones.getDelMedio()) / 1;
		} catch (IndexOutOfBoundsException e) {
			velocidad = 0.0;
		}
		return velocidad;
	}

	public boolean estaSubiendo() {
		double dif;
		try {
			dif = alturas.getMasNuevo() - alturas.getDelMedio();
		} catch (IndexOutOfBoundsException e) {
			dif = 0.0;
		}
		return (dif > 0);
	}

	public double getAltura() {
		return sp.alturaBalon();
	}

	// public Posicion getPosicionRivalMasCercano() {
	// return sp.rivales()[getPosicion().indiceMasCercano(sp.rivales())];
	// }

	public Posicion getPosicionRivalMasCercano(int indice) {
		return sp.rivales()[getPosicion().indicesMasCercanos(sp.rivales())[indice]];
	}

	public int[] indicesRivalesSegunDistancia(double distancia) {
		int[] rivalesCercanos = getPosicion().indicesMasCercanos(sp.rivales());
		int cont = 0;
		for (int i = 0; i < rivalesCercanos.length; i++) {
			if (getPosicion().distancia(sp.rivales()[rivalesCercanos[i]]) < distancia) {
				cont++;
			} else {
				break;
			}
		}
		int[] tmp = new int[cont];
		for (int i = 0; i < cont; i++) {
			tmp[i] = rivalesCercanos[i];
		}
		return tmp;
	}

	public boolean seAcerca(Posicion jugador, double distnaciaPelota) {
		if (jugador.norma(getPosicion()) < jugador.norma(getPosicionAnterior()) && jugador.distancia(getPosicion()) < distnaciaPelota) {
			return true;
		}
		return false;
	}

	public Posicion getPosicionJugadorEquipoMasCercano() {
		return sp.misJugadores()[getPosicion().indicesMasCercanos(sp.misJugadores())[0]];
	}

	public int getIndiceMasCercano() {
		return sp.balon().indiceMasCercano(sp.misJugadores());
	}

	public Posicion getPosicionFuturo(int iteracionesFuturas) {
		double xActual, yActual, zActual, varX, varY, varZ, x = 0, y = 0, zSimulado;
		try {
			zSimulado = 0;// this.getAltura();
			xActual = posiciones.getMasNuevo().getX();
			yActual = posiciones.getMasNuevo().getY();
			zActual = alturas.getMasNuevo();
			varX = xActual - posiciones.getDelMedio().getX();
			varY = yActual - posiciones.getDelMedio().getY();
			varZ = zActual - alturas.getDelMedio();
			for (int i = 0; i < iteracionesFuturas; i++) {
				if (this.getAltura() > 0) {
					zSimulado = Utiles.redondeaMultiplo(zSimulado + varZ, Constantes.G);
					varZ = Utiles.redondeaMultiplo(varZ - Constantes.G, Constantes.G);
					varX = varX * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					varY = varY * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					if (zSimulado == 0) {
						varZ = (-varZ - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
						varZ = Utiles.redondeaMultiplo(varZ, Constantes.G);
					}
				} else {
					varX = varX * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
					varY = varY * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
				}
				x = x + varX;
				y = y + varY;
			}
			return new Posicion(xActual + x, yActual + y);
		} catch (IndexOutOfBoundsException e) {
			return new Posicion();
		}
	}

	private boolean sucedeGol(Posicion balon, double balonDx0, double balonDz0, double balonDy0, double alturaBalon) {
		double posY = Constantes.LARGO_CAMPO_JUEGO / 2d;
		if (Math.abs(balon.getY()) > posY) {
			double posX = (balonDx0 / balonDy0) * (posY - balon.getY()) + balon.getX();
			// + 0.02 para mayor seguridad de gol
			if (alturaBalon > Constantes.ALTURA_CONTROL_BALON + 0.02d && alturaBalon <= Constantes.ALTO_ARCO - 0.02d) {
				double abs = Math.abs(posX);
				if (abs < Constantes.LARGO_ARCO / 2d - Constantes.RADIO_BALON) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean cumpleObjetivo(Posicion balon, double balonDx0, double balonDz0, double balonDy0, double alturaBalon, Posicion posicionDestino,
			double alturaDestino) {
		double posY = posicionDestino.getY();
		if (balon.getY() > posY - 2 && balon.getY() < posY + 2) {
			double posX = (balonDx0 / balonDy0) * (posY - balon.getY()) + balon.getX();
			if (alturaBalon < alturaDestino && alturaBalon > alturaDestino - 1) {
				if (balon.getX() > posX - 2 && balon.getX() < posX + 2) {
					return true;
				}
			}
		}
		return false;
	}

	public double anguloGolpeDePase(int indiceJugador, double fuerzaRemate, Posicion posicionDestino, double alturaDestino) {

		double angulo;

		// mi variable
		double anguloVertical;

		JugadorDetalle jugador;
		double balonDz = 0, balonDx = 0, balonDy = 0;
		double vel;
		double angVer;
		// posicion actual de la pelota
		double x, y, z;
		boolean suelo;

		jugador = sp.detalleMisJugadores()[indiceJugador];

		// calcula el angulo
		angulo = getPosicion().angulo(posicionDestino);
		// comienzo en angulo 10
		anguloVertical = 10;

		while (anguloVertical <= 60) {
			// posicion actual de la pelota
			x = getPosicion().getX();
			y = getPosicion().getY();
			z = getAltura();
			anguloVertical = anguloVertical + (0.5);
			// calcula la velocidad del remate
			vel = fuerzaRemate * Constantes.getVelocidadRemate(jugador.getRemate());

			angVer = Math.min(anguloVertical, Constantes.ANGULO_VERTICAL_MAX);
			angVer = Math.max(angVer, 0);
			angVer = angVer * (Math.PI / 180d);
			if (vel != 0) {
				// si el remate tiene velocidad
				vel = vel * Math.cos(angVer);
				balonDz = Utiles.redondeaMultiplo(vel * Math.sin(angVer), Constantes.G);
				// calcula la velocidad en el plano x/y
				balonDx = Math.cos(angulo) * vel;
				balonDy = Math.sin(angulo) * vel;
			}

			for (int i = 0; i < 20; i++) {
				suelo = false;
				// condicion para que se arrastre
				if (!suelo && z == 0 && balonDz >= 0 && balonDz < Constantes.G * 3) {
					suelo = true;
				}

				x = x + balonDx;
				y = y + balonDy;

				if (!suelo) {
					// si patea por aire
					z = Utiles.redondeaMultiplo(z + balonDz, Constantes.G);
					balonDz = Utiles.redondeaMultiplo(balonDz - Constantes.G, Constantes.G);
					balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					if (z == 0) {
						balonDz = (-balonDz - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
						balonDz = Utiles.redondeaMultiplo(balonDz, Constantes.G);
					}
				} else {
					// si patea por tierra
					balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
					balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
				}
				if (cumpleObjetivo(new Posicion(x, y), balonDx, balonDz, balonDy, z, posicionDestino, alturaDestino)) {
					return anguloVertical;
				}

			}
		}
		return -1;
	}

	private Posicion getPosicionDeGol() {
		final double DIF = 0.3d;
		double distanciaPosteIzq, distanciaPosteDerecho, x, y;
		Posicion arqueroRival = sp.rivales()[getIndiceArqueroRival()];
		distanciaPosteIzq = arqueroRival.norma(Constantes.posteIzqArcoSup);
		distanciaPosteDerecho = arqueroRival.norma(Constantes.posteDerArcoSup);
		y = Constantes.centroArcoSup.getY();
		
		if (this.getPosicion().getX() < 0) {
			// arquero rival esta en el medio
			if (Math.abs(arqueroRival.getX()) < Constantes.DISTANCIA_CONTROL_BALON_PORTERO) {
				x = Constantes.posteIzqArcoSup.getX() + DIF;
			} else {// arquero rival NO esta en el medio
				if (this.getPosicion().getX() < Constantes.posteIzqArcoSup.getX()) {
					x = Constantes.posteIzqArcoSup.getX() + DIF;
				} else {
					if (distanciaPosteIzq < distanciaPosteDerecho)
						x = Constantes.posteDerArcoSup.getX() - DIF;
					else
						x = Constantes.posteIzqArcoSup.getX() + DIF;
				}
			}
		} else {
			// arquero rival esta en el medio
			if (Math.abs(arqueroRival.getX()) < Constantes.DISTANCIA_CONTROL_BALON_PORTERO) {// esta en el medio
				x = Constantes.posteDerArcoSup.getX() - DIF;
			} else {
				if (this.getPosicion().getX() < Constantes.posteIzqArcoSup.getX()) {
					x = Constantes.posteIzqArcoSup.getX() + DIF;
				} else {
					if (distanciaPosteIzq < distanciaPosteDerecho)
						x = Constantes.posteDerArcoSup.getX() - DIF;
					else
						x = Constantes.posteIzqArcoSup.getX() + DIF;
				}
			}
		}

		return new Posicion(x, y);
	}

	public double getAnguloGolpeDeGol(int indiceJugador, double fuerzaRemate) {

		double angulo;
		Posicion posicionDestino;
		if (distanciaAlArco() < 20d)
			posicionDestino = getPosicionDeGol();
		else
			posicionDestino = Constantes.centroArcoSup;

		// mi variable
		double anguloVertical;

		JugadorDetalle jugador;
		double balonDz = 0, balonDx = 0, balonDy = 0;
		double vel;
		double angVer;
		// posicion actual de la pelota
		double x, y, z;
		boolean suelo;

		jugador = sp.detalleMisJugadores()[indiceJugador];

		// calcula el angulo
		angulo = getPosicion().angulo(posicionDestino);
		// comienzo en angulo 10
		anguloVertical = 10;

		while (anguloVertical <= 60) {
			// posicion actual de la pelota
			x = getPosicion().getX();
			y = getPosicion().getY();
			z = getAltura();
			anguloVertical = anguloVertical + (0.5);
			// calcula la velocidad del remate
			vel = fuerzaRemate * Constantes.getVelocidadRemate(jugador.getRemate());

			angVer = Math.min(anguloVertical, Constantes.ANGULO_VERTICAL_MAX);
			angVer = Math.max(angVer, 0);
			angVer = angVer * (Math.PI / 180d);
			if (vel != 0) {
				// si el remate tiene velocidad
				vel = vel * Math.cos(angVer);
				balonDz = Utiles.redondeaMultiplo(vel * Math.sin(angVer), Constantes.G);
				// calcula la velocidad en el plano x/y
				balonDx = Math.cos(angulo) * vel;
				balonDy = Math.sin(angulo) * vel;
			}

			for (int i = 0; i < 20; i++) {
				suelo = false;
				// condicion para que se arrastre
				if (!suelo && z == 0 && balonDz >= 0 && balonDz < Constantes.G * 3) {
					suelo = true;
				}

				x = x + balonDx;
				y = y + balonDy;

				if (!suelo) {
					// si patea por aire
					z = Utiles.redondeaMultiplo(z + balonDz, Constantes.G);
					balonDz = Utiles.redondeaMultiplo(balonDz - Constantes.G, Constantes.G);
					balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					if (z == 0) {
						balonDz = (-balonDz - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
						balonDz = Utiles.redondeaMultiplo(balonDz, Constantes.G);
					}
				} else {
					// si patea por tierra
					balonDx = balonDx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
					balonDy = balonDy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
				}
				if (sucedeGol(new Posicion(x, y), balonDx, balonDz, balonDy, z)) {
					return anguloVertical;
				}
			}
		}
		return 30;
	}

	public boolean estaAMetrosLateral(double distanciaLineaLateral) {
		if (Math.abs(this.getPosicion().getX()) > (Constantes.ANCHO_CAMPO_JUEGO / 2) - distanciaLineaLateral) {
			return true;
		}
		return false;
	}

	public double getAlturaFutura(int iteracion) {// ??????
		return getPosicion().getY() + getVelocidad() * iteracion + 0.5 * Constantes.G * Math.pow(iteracion, 2);
	}

	public boolean esPateadaPorRival() {
		if (cambiaDeDireccion()) {
			Posicion pelotaAlMomentoDePatear = posiciones.getDelMedio();
			int indicePateador = pelotaAlMomentoDePatear.indiceMasCercano(sp.rivales());
			if (pelotaAlMomentoDePatear.distancia(sp.rivales()[indicePateador]) <= Constantes.DISTANCIA_CONTROL_BALON)
				return true;
		}
		return false;
	}

	public double getPendienteDireccionActual() {
		double m;
		try {
			m = Utiles.getPendiente(posiciones.getDelMedio(), posiciones.getMasNuevo());
		} catch (IndexOutOfBoundsException e) {
			m = Double.NaN;
		}
		return m;
	}

	public double getPendienteDireccionAnterior() {
		double m;
		try {
			m = Utiles.getPendiente(posiciones.getMasAntiguo(), posiciones.getDelMedio());
		} catch (IndexOutOfBoundsException e) {
			m = Double.NaN;
		}
		return m;
	}

	public boolean cambiaDeDireccion() {
		double m1, m2;
		m1 = getPendienteDireccionActual();
		m2 = getPendienteDireccionAnterior();
		m1 = Utiles.redondear(m1, 1000);
		m2 = Utiles.redondear(m2, 1000);
		if (m1 != m2 && !(Double.isNaN(m1) && Double.isNaN(m2)))
			return true;
		else
			return false;
	}

	public double distanciaAlArco() {
		return getPosicion().distancia(Constantes.centroArcoSup);
	}

	@Override
	public String toString() {
		return getPosicion().getX() + ", " + getPosicion().getY() + ", " + getAltura() + " "
				+ Math.sqrt(getPosicion().norma(Constantes.centroCampoJuego));
	}

}
